/*
 * Copyright [2022] [https://www.xiaonuo.vip]
 *
 * Snowy采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改Snowy源码头部的版权声明。
 * 3.本项目代码可免费商业使用，商业使用请保留源码和相关描述文件的项目出处，作者声明等。
 * 4.分发源码时候，请注明软件出处 https://www.xiaonuo.vip
 * 5.不可二次分发开源参与同类竞品，如有想法可联系团队xiaonuobase@qq.com商议合作。
 * 6.若您的项目无法满足以上几点，需要更多功能代码，获取Snowy商业授权许可，请在官网购买授权，地址为 https://www.xiaonuo.vip
 */
package vip.xiaonuo.monte.modular.simulate.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.poi.util.StringUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vip.xiaonuo.common.enums.CommonSortOrderEnum;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.page.CommonPageRequest;
import vip.xiaonuo.monte.modular.calculateLog.entity.MonteCalculateLog;
import vip.xiaonuo.monte.modular.calculateLog.service.MonteCalculateLogService;
import vip.xiaonuo.monte.modular.simulate.entity.MonteSimulate;
import vip.xiaonuo.monte.modular.simulate.enums.SimulateParamsTypeEnum;
import vip.xiaonuo.monte.modular.simulate.mapper.MonteSimulateMapper;
import vip.xiaonuo.monte.modular.simulate.param.MonteSimulateAddParam;
import vip.xiaonuo.monte.modular.simulate.param.MonteSimulateEditParam;
import vip.xiaonuo.monte.modular.simulate.param.MonteSimulateIdParam;
import vip.xiaonuo.monte.modular.simulate.param.MonteSimulatePageParam;
import vip.xiaonuo.monte.modular.simulate.result.MonteSimulateResult;
import vip.xiaonuo.monte.modular.simulate.service.MonteSimulateService;
import vip.xiaonuo.monte.modular.simulateParams.entity.MonteSimulateParams;
import vip.xiaonuo.monte.modular.simulateParams.service.MonteSimulateParamsService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * monteSimulateService接口实现类
 *
 * @author lihao
 * @date  2025/05/11 17:01
 **/
@Service
public class MonteSimulateServiceImpl extends ServiceImpl<MonteSimulateMapper, MonteSimulate> implements MonteSimulateService {

    @Resource
    private MonteSimulateParamsService simulateParamsService;

    @Resource
    private MonteCalculateLogService monteCalculateLogService;

    @Override
    public Page<MonteSimulate> page(MonteSimulatePageParam monteSimulatePageParam) {
        QueryWrapper<MonteSimulate> queryWrapper = new QueryWrapper<MonteSimulate>().checkSqlInjection();
        if(ObjectUtil.isNotEmpty(monteSimulatePageParam.getName())) {
            queryWrapper.lambda().like(MonteSimulate::getName, monteSimulatePageParam.getName());
        }
        if(ObjectUtil.isAllNotEmpty(monteSimulatePageParam.getSortField(), monteSimulatePageParam.getSortOrder())) {
            CommonSortOrderEnum.validate(monteSimulatePageParam.getSortOrder());
            queryWrapper.orderBy(true, monteSimulatePageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
                    StrUtil.toUnderlineCase(monteSimulatePageParam.getSortField()));
        } else {
            queryWrapper.lambda().orderByAsc(MonteSimulate::getId);
        }
        return this.page(CommonPageRequest.defaultPage(), queryWrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(MonteSimulateAddParam addParam) {
        MonteSimulate monteSimulate = BeanUtil.toBean(addParam, MonteSimulate.class);
        this.save(monteSimulate);

        if(addParam.getParams() != null && !addParam.getParams().isEmpty()){
            List<MonteSimulateParams> params = addParam.getParams();
            for (MonteSimulateParams param : params) {
                param.setSimulateId(monteSimulate.getId());
            }
            simulateParamsService.saveBatch(params);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(MonteSimulateEditParam editParam) {
        MonteSimulate monteSimulate = this.queryEntity(editParam.getId());
        BeanUtil.copyProperties(editParam, monteSimulate);
        this.updateById(monteSimulate);

        simulateParamsService.remove(new LambdaQueryWrapper<MonteSimulateParams>()
                .eq(MonteSimulateParams::getSimulateId, monteSimulate.getId()));

        if(editParam.getParams() != null && !editParam.getParams().isEmpty()){
            List<MonteSimulateParams> params = editParam.getParams();
            for (MonteSimulateParams param : params) {
                //param.setId(null);
                param.setSimulateId(monteSimulate.getId());
            }
            simulateParamsService.saveBatch(params);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<MonteSimulateIdParam> monteSimulateIdParamList) {
        // 执行删除
        this.removeByIds(CollStreamUtil.toList(monteSimulateIdParamList, MonteSimulateIdParam::getId));
    }

    @Override
    public MonteSimulateResult detail(MonteSimulateIdParam monteSimulateIdParam) {
        MonteSimulateResult result = new MonteSimulateResult();
        MonteSimulate simulate = this.queryEntity(monteSimulateIdParam.getId());
        BeanUtils.copyProperties(simulate, result);

        List<MonteSimulateParams> params = simulateParamsService.list(new LambdaQueryWrapper<MonteSimulateParams>()
                .eq(MonteSimulateParams::getSimulateId, monteSimulateIdParam.getId()));
        result.setParams(params);

        List<MonteCalculateLog> logList = monteCalculateLogService.list(new LambdaQueryWrapper<MonteCalculateLog>()
                .eq(MonteCalculateLog::getSimulateId, monteSimulateIdParam.getId())
                .orderByDesc(MonteCalculateLog::getCreateTime));
        result.setLogList(logList);
        return result;
    }

    @Override
    public MonteSimulate queryEntity(String id) {
        MonteSimulate monteSimulate = this.getById(id);
        if(ObjectUtil.isEmpty(monteSimulate)) {
            throw new CommonException("monteSimulate不存在，id值为：{}", id);
        }
        return monteSimulate;
    }

    /**
     * 开始模拟
     *
     * @param param
     */
    @Override
    @Transactional
    public void startSimulate(MonteSimulateEditParam param) {
        // 1、获得基本数据， 参数
        MonteSimulate monteSimulate = BeanUtil.toBean(param, MonteSimulate.class);
        if(param.getId() != null){
            this.updateById(monteSimulate);
        }else{
            this.save(monteSimulate);
        }

        simulateParamsService.remove(new LambdaQueryWrapper<MonteSimulateParams>()
                .eq(MonteSimulateParams::getSimulateId, monteSimulate.getId()));

        if(param.getParams() != null && !param.getParams().isEmpty()){
            List<MonteSimulateParams> simulateParams = param.getParams();
            for (MonteSimulateParams item : simulateParams) {
                item.setSimulateId(monteSimulate.getId());
            }
            simulateParamsService.saveBatch(param.getParams());

            // 2、根据参数进行生成
            Integer simulateNum = monteSimulate.getSimulateNum();

            StringBuilder vStr = new StringBuilder();
            StringBuilder rStr = new StringBuilder();
            Integer trueNum = 0;
            for(int a=0; a< simulateNum; a++){
                Map<String, Double> variables = new HashMap<>();
                for (MonteSimulateParams params : param.getParams()) {
                    double randomValue = 0.0;
                    if(params.getDistributionType().equals(SimulateParamsTypeEnum.VARIABLE.getValue())){
                        NormalDistribution distribution = new NormalDistribution(params.getMeanValue(),
                                params.getSdValue());
                        randomValue = distribution.sample();
                    }else{
                        // 常量
                        randomValue = params.getMeanValue();
                    }
                    variables.put(params.getParamKey(), randomValue);
                }
                vStr.append(JSONUtil.toJsonStr(variables) + ",");
                double result = evaluate(monteSimulate.getFormula(), variables);
                rStr.append(result);
                rStr.append(",");

                if(result >= param.getResultMin() && result <= param.getResultMax()){
                    trueNum++;
                }
            }

            // 3、生成完成后写入结果
            MonteCalculateLog calculateLog = new MonteCalculateLog();
            calculateLog.setSimulateId(param.getId());
            calculateLog.setSimulateData(JSONUtil.toJsonStr(param));
            calculateLog.setParams(vStr.toString());
            calculateLog.setResult(rStr.toString());
            calculateLog.setTrueNum(trueNum);
            calculateLog.setSimulateNum(simulateNum);
            monteCalculateLogService.save(calculateLog);
        }

    }

    public double evaluate(String expression, Map<String, Double> variables) {
        // 去除所有空格
        String expr = expression.replaceAll("\\s+", "");

        // 分割表达式为tokens（变量名和运算符）
        List<String> tokens = splitTokens(expr);

        // 校验tokens的数量是否为奇数（例如，操作数、运算符、操作数...）
        if (tokens.size() % 2 == 0) {
            throw new IllegalArgumentException("无效的表达式格式");
        }

        // 将变量名替换为对应的值，并转换为操作数和运算符的列表
        List<Object> processedTokens = new ArrayList<>();
        for (String token : tokens) {
            if (isOperator(token)) {
                processedTokens.add(token);
            } else {
                Double value = variables.get(token);
                if (value == null) {
                    throw new IllegalArgumentException("变量 '" + token + "' 不存在");
                }
                processedTokens.add(value);
            }
        }

        // 处理乘除运算
        List<Object> afterMulDiv = processMulDiv(processedTokens);

        // 处理加减运算
        return processAddSub(afterMulDiv);
    }

    // 使用正则表达式分割tokens
    private static List<String> splitTokens(String expr) {
        List<String> tokens = new ArrayList<>();
        Pattern pattern = Pattern.compile("([a-zA-Z0-9]+)|([-+*/])");
        Matcher matcher = pattern.matcher(expr);
        while (matcher.find()) {
            String token = matcher.group();
            tokens.add(token);
        }
        return tokens;
    }

    // 判断是否为运算符
    private static boolean isOperator(String token) {
        return token.matches("[-+*/]");
    }

    // 处理乘除运算
    private static List<Object> processMulDiv(List<Object> tokens) {
        List<Object> result = new ArrayList<>();
        int i = 0;
        while (i < tokens.size()) {
            Object current = tokens.get(i);
            if (current instanceof String) {
                String op = (String) current;
                if (op.equals("*") || op.equals("/")) {
                    // 取出前一个操作数
                    if (result.isEmpty()) {
                        throw new IllegalArgumentException("无效的表达式：运算符在开头");
                    }
                    double left = (Double) result.remove(result.size() - 1);
                    i++; // 移动到下一个元素（操作数）
                    if (i >= tokens.size()) {
                        throw new IllegalArgumentException("无效的表达式：运算符后无操作数");
                    }
                    Object rightObj = tokens.get(i);
                    if (!(rightObj instanceof Double)) {
                        throw new IllegalArgumentException("无效的表达式：运算符后不是数字");
                    }
                    double right = (Double) rightObj;
                    double calc = calculate(left, right, op);
                    result.add(calc);
                    i++; // 处理完毕，i前进
                } else {
                    // 加减运算符直接加入结果列表
                    result.add(op);
                    i++;
                }
            } else {
                result.add(current);
                i++;
            }
        }
        return result;
    }

    // 处理加减运算
    private static double processAddSub(List<Object> tokens) {
        if (tokens.isEmpty()) {
            return 0.0;
        }
        if (tokens.size() % 2 == 0) {
            throw new IllegalArgumentException("无效的表达式：操作数与运算符不匹配");
        }
        double res = (Double) tokens.get(0);
        for (int i = 1; i < tokens.size(); i += 2) {
            String op = (String) tokens.get(i);
            double num = (Double) tokens.get(i + 1);
            res = calculate(res, num, op);
        }
        return res;
    }

    // 执行基础运算
    private static double calculate(double a, double b, String op) {
        switch (op) {
            case "+":
                return a + b;
            case "-":
                return a - b;
            case "*":
                return a * b;
            case "/":
                if (b == 0) {
                    throw new ArithmeticException("除以零错误");
                }
                return a / b;
            default:
                throw new IllegalArgumentException("未知运算符: " + op);
        }
    }


}
